package com.gt.web.api.exception;
import cn.hutool.core.util.StrUtil;
import com.gt.web.api.common.R;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.sql.SQLException;
import java.util.stream.Collectors;

/**
 * <p>
 * 全局异常处理器
 * </p>
 *
 * @date 2019/5/2
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionAdvice {

    @Value("${remove-exception-linefeed:true}")
    private boolean removeExceptionLinefeed;

    /**
     * Controller上一层相关异常
     *
     * @param e 异常
     * @return 异常结果
     */
    @ExceptionHandler({NoHandlerFoundException.class
            ,HttpRequestMethodNotSupportedException.class
            ,HttpMediaTypeNotSupportedException.class
            ,HttpMediaTypeNotAcceptableException.class
            ,MissingPathVariableException.class
            ,MissingServletRequestParameterException.class
            ,TypeMismatchException.class
            ,HttpMessageNotReadableException.class
            ,HttpMessageNotWritableException.class
            ,ServletRequestBindingException.class
            ,ConversionNotSupportedException.class
            ,MissingServletRequestPartException.class
            ,AsyncRequestTimeoutException.class})
    public R handleServletException(Exception e) {
        log.warn("请求参数异常:{}", ExceptionUtils.getStackTrace(e));
        return R.fail(400,"参数有误");
    }

    /**
     * 业务异常
     *
     * @param e 异常
     * @return 异常结果
     */
    @ExceptionHandler(value = BusinessException.class)
    public R handleBusinessException(BusinessException e) {

        log.error("业务处理异常:{}", ExceptionUtils.getStackTrace(e));
        return  R.fail(e.getCode(), e.getMessage());
    }

    /**
     * 处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是javax.validation.ConstraintViolationException
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public R ConstraintViolationExceptionHandler(ConstraintViolationException e) {

        log.error("参数异常:{}", ExceptionUtils.getStackTrace(e));
        String message = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining());
        if (!StrUtil.isBlank(message)) {
            return  R.fail(400, message);
        }
        return R.fail(400,"参数异常");
    }

    @ExceptionHandler(value = {MethodArgumentNotValidException.class, BindException.class})
    public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {

        log.error("参数异常:{}", e);
        BindingResult result = e.getBindingResult();
        if (result != null && result.hasErrors()) {
            return  R.fail(400, new StringBuilder()
                    .append(result.getFieldError().getDefaultMessage()).toString()+"_不能空");
        }
        return R.fail(400, "参数异常");

    }

    @ExceptionHandler(value = {IllegalArgumentException.class})
    public R handleIllegalArgumentException(IllegalArgumentException e) {
        log.error("参数异常:{}", ExceptionUtils.getStackTrace(e));
        return  R.fail(400, e.getMessage());
    }

    @ExceptionHandler(value = SQLException.class)
    public R handleSQLException(SQLException e) {
        log.error("处理发生sql异常:{}", ExceptionUtils.getStackTrace(e));
        return R.fail(400, ExceptionUtils.getStackTrace(e));
    }

    /**
     * 未定义异常
     *
     * @param e 异常
     * @return 异常结果
     */
    @ExceptionHandler(value = Exception.class)
    public R handleException(Exception e) {

        if (e != null && (e instanceof SQLException || e instanceof DataAccessException)) {
            log.error("处理发生sql异常:{}", ExceptionUtils.getStackTrace(e));
            return  R.fail(500, "系统异常_SQL");
        }
        log.error("系统异常:{}", ExceptionUtils.getStackTrace(e));
        return R.fail(500, "服务开小差了");
    }

}
